package com.lhq.tag_tales.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
import com.lhq.tag_tales.annotation.CheckIsCurrentUser;
import com.lhq.tag_tales.common.ErrorCodeEnum;
import com.lhq.tag_tales.common.IsCurrentUserEnum;
import com.lhq.tag_tales.exception.BusinessException;
import com.lhq.tag_tales.mapper.TeamMapper;
import com.lhq.tag_tales.model.domain.Team;
import com.lhq.tag_tales.model.domain.TeamDetails;
import com.lhq.tag_tales.model.domain.User;
import com.lhq.tag_tales.model.domain.dto.TeamQuery;
import com.lhq.tag_tales.model.domain.request.TeamAddRequest;
import com.lhq.tag_tales.model.domain.request.TeamJoinRequest;
import com.lhq.tag_tales.model.domain.request.TeamUpdateRequest;
import com.lhq.tag_tales.model.domain.vo.TeamUserVO;
import com.lhq.tag_tales.model.domain.vo.UserVO;
import com.lhq.tag_tales.service.TeamDetailsService;
import com.lhq.tag_tales.service.TeamService;
import com.lhq.tag_tales.service.UserService;
import com.lhq.tag_tales.utils.BeanUtilsCustom;
import com.lhq.tag_tales.utils.PasswordUtils;
import com.lhq.tag_tales.utils.UserUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.lhq.tag_tales.constant.UserConstant.VALID_ACCOUNT_REGEX;

/**
 * @author L_curiosity
 * @description 针对表【team(队伍表)】的数据库操作Service实现
 * @createDate 2024-11-15 20:54:55
 */
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
        implements TeamService {
    private static final int MAX_MEMBER_NUMBER = 20;
    private static final int MIN_MEMBER_NUMBER = 3;
    //最长有效期
    private static final int MAX_EXPIRE_TIME = 6;
    //最多创建队伍数
    private static final int MAX_TEAM_NUMBER = 5;
    //最大加入数量
    private static final int MAX_JOIN_NUMBER = 10;
    @Autowired
    SensitiveWordBs sensitiveWordBs;
    @Resource
    private TeamMapper teamMapper;
    @Resource
    private UserService userService;
    @Resource
    private TeamDetailsService teamDetailsService;

    /**
     * 添加队伍
     * 部分校验在AOP中进行
     *
     * @param teamAddRequest 队伍信息
     * @return
     */
    @Transactional
    @CheckIsCurrentUser(value = IsCurrentUserEnum.CHECK, type = TeamAddRequest.class)
    public Long addTeam(TeamAddRequest teamAddRequest) {
        //1. 校验队伍的基础信息
        String name = teamAddRequest.getName();
        String password = teamAddRequest.getPassword();
        User user = UserUtils.getCurrentUser();
        Long userId = user.getId();
        Team team = new Team();
        try {
            BeanUtilsCustom.copyProperties(teamAddRequest, team);
        } catch (IllegalAccessException e) {
            log.error("属性复制失败", e);
        }
        checkTeamInfo(team, name, password, userId);

        //2. 校验队伍名称是否重复
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", name);
        boolean exists = teamMapper.exists(queryWrapper);
        if (exists) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "队伍名称重复,请重新命名");
        }

        //3. 普通用户一天只能创建一个队伍
        if (user.getRole() != 1) {
            LocalDate today = LocalDate.now();
            LocalDateTime startOfDay = LocalDateTime.of(today, LocalTime.MIN);
            LocalDateTime endOfDay = LocalDateTime.of(today, LocalTime.MAX);
            QueryWrapper<Team> queryByCreateDate = new QueryWrapper<>();
            queryByCreateDate.between("create_time", startOfDay, endOfDay);
            long count = teamMapper.selectCount(queryByCreateDate);
            if (count >= 1) {
                throw new BusinessException(ErrorCodeEnum.NO_AUTH, "普通用户每天只能创建一个队伍");
            }
            //普通用户最多创建5个队伍
            LambdaQueryWrapper<Team> queryByCreator = new LambdaQueryWrapper<>();
            queryByCreator.eq(Team::getCreator, userId);
            Long countTeamNum = teamMapper.selectCount(queryByCreator);
            if (countTeamNum >= MAX_TEAM_NUMBER) {
                throw new BusinessException(ErrorCodeEnum.NO_AUTH, "普通用户最多创建5个队伍");
            }
        }

        //4. 判断创建队伍的类型，如果是私密，则对密码进行加密
        if (teamAddRequest.getStatus().equals(2)) {
            //密码加密
            teamAddRequest.setPassword(PasswordUtils.encryptPassword(password));
        }

        //4. 插入队伍数据
        //设置当前队伍数量为 1
        team.setCurrentNum(1);
        long length = teamMapper.insert(team);
        if (length <= 0) {
            throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，创建队伍失败");
        }
        //5. 插入组队信息
        TeamDetails teamDetails = new TeamDetails();
        //用户id
        teamDetails.setUserId(userId);
        //队伍id
        teamDetails.setTeamId(team.getId());
        //加入时间
        teamDetails.setJoinTime(new Date());
        boolean save = teamDetailsService.save(teamDetails);
        if (!save) {
            throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，插入组队明细失败");
        }
        return length;
    }


    private void checkTeamInfo(Team team, String name, String password, Long userId) {
        team.setPassword(password);
        team.setName(name);
        team.setCreator(userId);
        checkTeamInfo(team);
    }

    private void checkTeamInfo(Team team, Integer maxNum, Long teamId) {
        team.setMaxNum(maxNum);
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "队伍id非法");
        }
        team.setId(teamId);
        checkTeamInfo(team);
    }

    private void checkTeamInfo(Team team) {
        //1. 校验队伍的人数不能超过20，不能小于3
        Integer num = team.getMaxNum();
        if (num == null) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "组队人数不能为空");
        }
        if (num > MAX_MEMBER_NUMBER || num < MIN_MEMBER_NUMBER) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "队伍人数必须在3-20之间");
        }
        //2. 判断过期时间 不能小于当前时间
        Date expireTime = team.getExpireTime();
        Date now = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        calendar.add(Calendar.MONTH, MAX_EXPIRE_TIME);
        Date maxExpireTime = calendar.getTime();
        if (expireTime.after(maxExpireTime) || expireTime.before(now)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "队伍过期时间必须大于当前时间且不能超过6个月");
        }
        //3. 密码校验
        String password = team.getPassword();
        //是否私密
        Integer status = team.getStatus();
        //只有队伍设置为私密时才校验密码
        if (status == 3) {
            if (StringUtils.isBlank(password)) {
                throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "密码不能为空");
            }
            int length = password.length();
            if (length < 4 || length > 12) {
                throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "密码长度必须在4-12之间");
            }
            //合法性校验
            if (!password.matches(VALID_ACCOUNT_REGEX)) {
                throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "密码不能包含特殊字符");
            }
        } else {
            //如果队伍状态非私密，则密码置空
            team.setPassword(null);
        }
        //4. 队伍名称敏感词校验
        String name = team.getName();
        if (StringUtils.isBlank(name)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "队伍名称不能为空");
        }
        if (sensitiveWordBs.contains(name)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "队伍名称含有敏感词,请重新命名");
        }
    }

    /**
     * 根据id删除队伍
     * 部分校验在AOP中进行
     *
     * @param id 队伍id
     */
    @Transactional
    @CheckIsCurrentUser(value = IsCurrentUserEnum.CHECK, type = Long.class)
    public void deleteTeamById(Long id) {
        //删除队伍
        boolean isSuccess = removeById(id);
        if (!isSuccess) {
            throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，删除队伍失败");
        }
        //关联删除组队明细
        QueryWrapper<TeamDetails> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("team_id", id);
        boolean result = teamDetailsService.remove(queryWrapper);
        if (!result) {
            throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，删除组队明细失败");
        }
    }

    @Override
    @CheckIsCurrentUser(value = IsCurrentUserEnum.CHECK, type = TeamUpdateRequest.class)
    public void updateTeam(TeamUpdateRequest teamUpdateRequest) {
        Integer maxNum = teamUpdateRequest.getMaxNum();
        //1. 队伍信息基础校验
        Long teamId = teamUpdateRequest.getId();
        Team team = new Team();
        try {
            BeanUtilsCustom.copyProperties(teamUpdateRequest, team);
        } catch (IllegalAccessException e) {
            log.error("属性拷贝失败", e);
        }
        checkTeamInfo(team, maxNum, teamId);
        //2.队伍最大容量必须大于已经加入队伍的人数
        Integer currentNum = team.getCurrentNum();
        if (currentNum > maxNum) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "队伍人数不能小于当前人数");
        }
        //更新队伍信息
        int index = teamMapper.updateById(team);
        if (index <= 0) {
            throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，更新队伍信息失败");
        }
    }

    /**
     * 搜索队伍列表分页查询
     *
     * @param teamQuery 队伍查询条件
     * @return 分页查询结果
     */
    @Override
    public Page<TeamUserVO> searchTeamsPage(TeamQuery teamQuery) {
        int pageNum = teamQuery.getPageNum();
        int pageSize = teamQuery.getPageSize();
        List<TeamUserVO> teamQueryList = buildQueryTeam(teamQuery);
        Page<TeamUserVO> teamQueryPage = Page.of((long) (pageNum - 1) * pageSize, pageSize, teamQueryList.size());
        teamQueryPage.setRecords(teamQueryList);
        return teamQueryPage;
    }

    @Override
    public List<TeamUserVO> listTeams(TeamQuery teamQuery) {
        return buildQueryTeam(teamQuery);
    }

    @Override
    @Transactional
    public void joinTeam(TeamJoinRequest teamJoinRequest) {
        //1. 判断要加入的队伍是否存在
        Long teamId = teamJoinRequest.getTeamId();
        Team team = getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCodeEnum.NULL_ERROR, "队伍不存在");
        }
        //2. 判断队伍是否是个人的
        Integer status = team.getStatus();
        if (status == 1) {
            throw new BusinessException(ErrorCodeEnum.NO_AUTH, "该队伍是私密队伍，请输入密码加入");
        }
        //3. 判断队伍是否是私密的，判断密码是否相等
        String password = teamJoinRequest.getPassword();
        if (status == 2) {
            if (StringUtils.isBlank(password)) {
                throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "密码不能为空");
            }
            if (!password.matches(VALID_ACCOUNT_REGEX)) {
                throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "密码不能包含特殊字符");
            }
            //密码加密
            password = PasswordUtils.encryptPassword(password);
            if (!password.equals(team.getPassword())) {
                throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "密码不正确");
            }
        }
        //4. 判断队伍是否过期
        Date expireTime = team.getExpireTime();
        if (expireTime != null && expireTime.before(new Date())) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "队伍已过期，无法加入");
        }
        //5. 判断队伍是否满员
        Integer currentNum = team.getCurrentNum();
        Integer maxNum = team.getMaxNum();
        if (currentNum >= maxNum) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "队伍已满员，无法加入");
        }
        //6. 判断加入队伍的数量是否超过上限
        User currentUser = UserUtils.getCurrentUser();
        if (currentUser == null) {
            throw new BusinessException(ErrorCodeEnum.NO_LOGIN, "未登录");
        }
        Long userId = currentUser.getId();
        QueryWrapper<TeamDetails> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        List<TeamDetails> list = teamDetailsService.list(queryWrapper);
        if (list.size() >= MAX_JOIN_NUMBER) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "加入队伍数量超过上限");
        }
        //7. 判断当前用户是否已经加入该队伍
        list.stream()
                .filter(teamDetails -> teamDetails.getTeamId().equals(teamId))
                .findFirst().ifPresent(teamDetails -> {
                    throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "当前用户已加入该队伍");
                });
        //9. 创建队伍明细
        TeamDetails teamDetails = new TeamDetails();
        teamDetails.setUserId(userId);
        teamDetails.setTeamId(teamId);
        teamDetails.setJoinTime(new Date());
        boolean result = teamDetailsService.save(teamDetails);
        if (!result) {
            throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，加入队伍失败");
        }
        //10. 更新队伍人数
        Integer newCurrentNum = team.getCurrentNum() + 1;
        team.setCurrentNum(newCurrentNum);
        int index = teamMapper.updateById(team);
        if (index <= 0) {
            throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，更新队伍人数失败");
        }
    }

    @Override
    @Transactional
    public void quitTeam(Long teamId) {
        //1. 判断队伍是否存在
        Team team = getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCodeEnum.NULL_ERROR, "队伍不存在");
        }
        //2. 判断队伍是否已过期
        Date expireTime = team.getExpireTime();
        if (expireTime != null && expireTime.before(new Date())) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "队伍已过期，无需退出");
        }
        //3. 判断是否是该队伍的成员
        User currentUser = UserUtils.getCurrentUser();
        Long userId = currentUser.getId();
        List<TeamDetails> teamDetailsList = getTeamDetailsByTeamId(teamId);
        Set<Long> userIdSet = teamDetailsList.stream().map(TeamDetails::getUserId).collect(Collectors.toSet());
        if (!userIdSet.contains(userId)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "你还不是该队伍的成员");
        }
        //4. 执行退出逻辑
        //4.1 队长退出，则自动将第二位成员作为队长
        if (team.getCreator().equals(userId)) {
            handleCaptainQuit(team, teamDetailsList, teamId);
        } else {
            //4.2 普通队员退出，直接删除该队员的记录
            handleMemberQuit(userId, teamId);
        }
        //5. 更新队伍数据
        updateTeamCurrentNum(team);
    }


    @Transactional
    @Override
    public void disbandTeam(Long teamId) {
        //1. 判断队伍是否存在
        Team team = getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCodeEnum.NULL_ERROR, "队伍不存在");
        }
        //2. 判断是否是队长或管理员
        User currentUser = UserUtils.getCurrentUser();
        Long userId = currentUser.getId();
        if (!team.getCreator().equals(userId)&&!currentUser.getRole().equals(1L)) {
            throw new BusinessException(ErrorCodeEnum.NO_AUTH, "无操作权限");
        }
        //3. 删除队伍信息
        boolean result = removeById(teamId);
        if (!result) {
            throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，删除队伍失败");
        }
        //4. 删除组队详情
        boolean remove = teamDetailsService.remove(new LambdaQueryWrapper<TeamDetails>().eq(TeamDetails::getTeamId, teamId));
        if (!remove) {
            throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，删除组队详情失败");
        }
    }

    private List<TeamDetails> getTeamDetailsByTeamId(Long teamId) {
        QueryWrapper<TeamDetails> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("team_id", teamId);
        return teamDetailsService.list(queryWrapper);
    }

    private void handleCaptainQuit(Team team, List<TeamDetails> teamDetailsList, Long teamId) {
        //找到第二位加入队伍的成员
        Optional<TeamDetails> secondMember = teamDetailsList.stream()
                .sorted(Comparator.comparing(TeamDetails::getJoinTime))
                .skip(1) // 跳过第一位（即队长）
                .findFirst(); // 取第二位

        if (secondMember.isPresent()) {
            TeamDetails newCaptain = secondMember.get();
            team.setCreator(newCaptain.getUserId());
            //更新队伍数据，设置新的队长
            int i = teamMapper.updateById(team);
            if (i <= 0) {
                throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，更新队伍数据失败");
            }
        } else {
            //队伍没有第二位成员，则将队伍和组队信息删除
            removeById(teamId);
            boolean remove = teamDetailsService.remove(new LambdaQueryWrapper<TeamDetails>().eq(TeamDetails::getTeamId, teamId));
            if (!remove) {
                throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，删除组队详情失败");
            }
        }
    }

    private void handleMemberQuit( Long userId, Long teamId) {
        boolean remove = teamDetailsService.remove(new LambdaQueryWrapper<TeamDetails>()
                .eq(TeamDetails::getUserId, userId)
                .eq(TeamDetails::getTeamId, teamId));
        if (!remove) {
            throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，删除组队详情失败");
        }
    }

    private void updateTeamCurrentNum(Team team) {
        Integer currentNum = team.getCurrentNum();
        team.setCurrentNum(currentNum - 1);
        int index = teamMapper.updateById(team);
        if (index <= 0) {
            throw new BusinessException(ErrorCodeEnum.DEFAULT_ERROR, "系统错误，更新队伍人数失败");
        }
    }



    private List<TeamUserVO> buildQueryTeam(TeamQuery teamQuery) {
        //1. 构建查询条件
        Team team = new Team();
        try {
            BeanUtilsCustom.copyProperties(teamQuery, team);
        } catch (IllegalAccessException e) {
            log.error("属性复制失败", e);
        }
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .eq(team.getId() != null && team.getId() > 0, "id", team.getId())
                .like(StringUtils.isNotBlank(team.getName()), "name", team.getName())
                .like(StringUtils.isNotBlank(team.getDescription()), "description", team.getDescription())
                .eq(team.getMaxNum() != null && team.getMaxNum() > 0, "max_num", team.getMaxNum())
                .eq(team.getCreator() != null && team.getCreator() > 0, "creator", team.getCreator())
                .ge("expire_time", LocalDateTime.now());  //筛选掉已经过期的队伍
        //根据用户身份信息构建查询条件
        User currentUser = UserUtils.getCurrentUser();
        Long role = currentUser.getRole();
        Integer status = team.getStatus();
        //根据状态查询且是管理员身份
        if (role == 1 && status != null) {
            queryWrapper.eq("status", status);
        }
        if (role == 0) {
            //如果是普通用户，直接设置为0
            queryWrapper.eq("status", 0);
        }
        // 2. 返回TeamQuery类型的列表
        List<Team> teamList = teamMapper.selectList(queryWrapper);
        if (teamList.isEmpty()) {
            return new ArrayList<>();
        }

        // 批量查询所有团队的成员信息（用in，减少和数据库交互次数）
        Set<Long> teamIds = teamList.stream().map(Team::getId).collect(Collectors.toSet());
        List<TeamDetails> allTeamDetailsList = teamDetailsService.list(new QueryWrapper<TeamDetails>().in("team_id", teamIds));

        // 将团队ID和成员信息映射到Map中
        Map<Long, List<TeamDetails>> teamDetailsMap = allTeamDetailsList.stream()
                .collect(Collectors.groupingBy(TeamDetails::getTeamId));

        return teamList.stream().map(item -> {
            TeamUserVO teamUserVO = new TeamUserVO();
            try {
                BeanUtilsCustom.copyProperties(item, teamUserVO);
            } catch (IllegalAccessException e) {
                log.error("属性复制失败", e);
            }

            // 获取当前团队的成员信息
            List<TeamDetails> teamDetailsList = teamDetailsMap.getOrDefault(item.getId(), Collections.emptyList());

            List<UserVO> members = teamDetailsList.stream().map(teamDetails -> {
                Long userId = teamDetails.getUserId();
                User user = userService.getById(userId);
                UserVO userVO = new UserVO();
                try {
                    BeanUtilsCustom.copyProperties(user, userVO);
                } catch (IllegalAccessException e) {
                    log.error("属性复制失败", e);
                }
                return userVO;
            }).collect(Collectors.toList());

            teamUserVO.setMembers(members);
            return teamUserVO;
        }).collect(Collectors.toList());
    }
}




